#pragma once
#include "CeBrainCommon.h"

class CNervus;
class CNervusAntenna
{
public:
	CNervusAntenna(bool bNegative, float nRate)
		:m_pNervus(NULL), m_pNervusAntennaPeer(NULL), m_bNegative(bNegative), next(NULL), m_fRate(nRate)
	{
		m_nSignalBuffer = 0;
	};
	CNervusAntenna(void)
		:m_pNervus(NULL), m_pNervusAntennaPeer(NULL), m_bNegative(false), next(NULL), m_fRate(1)
	{
		m_nSignalBuffer = 0;
	};
	~CNervusAntenna(void){
		if( next )
			delete next;
	};

	void Connect( CNervusAntenna* pNervusAntenna )
	{
		SetPeer( pNervusAntenna );
		pNervusAntenna->SetPeer( this );
	};

	void SetPeer( CNervusAntenna* pNervusAntenna )
	{
		m_pNervusAntennaPeer = pNervusAntenna;
	};

	CNervusAntenna* GetPeer( )
	{
		return m_pNervusAntennaPeer;
	};

	bool BreakCurrentConnection( void )
	{
		if( !GetPeer() )
			return false;
		else
		{
			GetPeer()->SetPeer( NULL );
			SetPeer( NULL );
			return true;
		}
	};

	void SetSignal( int nSignal )
	{
		m_nSignalBuffer = nSignal;
	};

	int GetSignal( void )
	{
		int nSignal = m_nSignalBuffer*m_fRate;
		return m_bNegative ? -nSignal : nSignal;
	};

	CNervus* GetMyNervus( void )
	{
		return m_pNervus;
	};

	void SetMyNervus( CNervus* n )
	{
		m_pNervus = n;
	};

	bool IsConnected( void )
	{
		return GetPeer() ? true : false;
	};

	void TransmitSignal( void )
	{
		if( GetPeer() )
			GetPeer()->SetSignal( GetSignal() );
	};

	CNervusAntenna* GetNextAntenna( void )
	{
		return next;
	};

	void SetNextAntenna( CNervusAntenna* p)
	{
		next = p;
	};

	float GetRate( void )
	{
		return m_fRate;
	};

	void SetRate( float nRate )
	{
		m_fRate = nRate;
	};

private:
	CNervusAntenna* m_pNervusAntennaPeer;
	CNervus* m_pNervus;
	int m_nSignalBuffer;
	bool m_bNegative;
	CNervusAntenna* next;
	float m_fRate;
};

typedef struct _NervusPOS{
int nLine;
int nIdx;
} NervusPOS;

class CNervus
{
public:
	CNervus(CNervusAntenna* ,CNervusAntenna*);
	virtual ~CNervus(void);

	void ProcessSignal( void );
	void DoSignalTransmition( void );

	void SetPosition( NervusPOS pos )
	{
		m_Pos = pos;
	};

	NervusPOS GetPosition( void )
	{
		return m_Pos; 
	};

	CNervusAntenna* GetFirstInputAntenna( void )
	{
		return m_pFirstInputNervusAntenna;
	};

	CNervusAntenna* GetFirstOutputAntenna( void )
	{
		return m_pFirstOutputNervusAntenna;
	};

	int GetInputAntennaCount( void )
	{
		int nCount=1;
		CNervusAntenna* pAntenna = m_pFirstInputNervusAntenna;
		while( pAntenna = pAntenna->GetNextAntenna() )
			nCount++;
		return nCount;
	}

	int GetOutputAntennaCount( void )
	{
		int nCount=1;
		CNervusAntenna* pAntenna = m_pFirstOutputNervusAntenna;
		while( pAntenna = pAntenna->GetNextAntenna() )
			nCount++;
		return nCount;
	}

	CNervusAntenna* GetInputAntenna( int nIdx )
	{
		CNervusAntenna* pAntenna = m_pFirstInputNervusAntenna;
		while( nIdx-- )
			pAntenna = pAntenna->GetNextAntenna();
		return pAntenna;
	}

	CNervusAntenna* GetOutputAntenna( int nIdx )
	{
		CNervusAntenna* pAntenna = m_pFirstOutputNervusAntenna;
		while( nIdx-- )
			pAntenna = pAntenna->GetNextAntenna();
		return pAntenna;
	}

	void AddInputAntenna( CNervusAntenna* pAntenna )
	{
		CNervusAntenna *pLastAntenna = m_pFirstInputNervusAntenna;
		while( pLastAntenna->GetNextAntenna() )
		{
			pLastAntenna = pLastAntenna->GetNextAntenna();
		}
		pLastAntenna->SetNextAntenna( pAntenna );
		pAntenna->SetMyNervus( this );
	}

	void AddOutputAntenna( CNervusAntenna* pAntenna )
	{
		CNervusAntenna *pLastAntenna = m_pFirstOutputNervusAntenna;
		while( pLastAntenna->GetNextAntenna() )
		{
			pLastAntenna = pLastAntenna->GetNextAntenna();
		}
		pLastAntenna->SetNextAntenna( pAntenna );
		pAntenna->SetMyNervus( this );
	}

	CNervus* GetOutputLinkedNervus( int nOutputIndex )
	{
		if( !GetOutputAntenna(nOutputIndex) )
			return NULL;
		if( GetOutputAntenna(nOutputIndex)->GetPeer() )
		{
			return GetOutputAntenna(nOutputIndex)->GetPeer()->GetMyNervus();
		}
		else
			return NULL;
	}

	void Connect( int nOutputIndex, CNervusAntenna* pTargetNervusAntenna )
	{
		if( !GetOutputAntenna(nOutputIndex) )
			return;
		if( GetOutputAntenna(nOutputIndex)->IsConnected() )
		{
			GetOutputAntenna(nOutputIndex)->BreakCurrentConnection();
		}
		GetOutputAntenna(nOutputIndex)->Connect( pTargetNervusAntenna );
	};

	bool ConnectNervus( int nOutputIndex, CNervus* pTargetNervus )
	{
		if( pTargetNervus->FindEmptyInputAntennna() )
		{
			if( GetOutputAntenna(nOutputIndex)->IsConnected() )
			{
				GetOutputAntenna(nOutputIndex)->BreakCurrentConnection();
			}
			GetOutputAntenna(nOutputIndex)->Connect( pTargetNervus->FindEmptyInputAntennna() );
			return true;
		}
		else
			return false;
	};

	CNervusAntenna* FindEmptyInputAntennna( void )
	{
		CNervusAntenna* pAntenna = m_pFirstInputNervusAntenna;
		do
		{
			if( !pAntenna->IsConnected() )
				return pAntenna;
		}while( pAntenna = pAntenna->GetNextAntenna() );

		return NULL;
	};

	bool CutConnection( int nOutputIndex )
	{
		return GetOutputAntenna(nOutputIndex)->BreakCurrentConnection();
	};

	CNervus* pNextInLine;

private:
	CNervusAntenna *m_pFirstInputNervusAntenna;
	CNervusAntenna *m_pFirstOutputNervusAntenna;
	NervusPOS m_Pos;
	bool m_bExcited;
	int m_nExcitedCount;
	int m_nSavedSignal;
	bool m_bRestrain;
	int m_nRestrainCount;
};

class CNervusNet
{
public:
	CNervusNet(void)
	{
		for( int j=0; j<NERVUS_NET_DEPTH; j++ )
		{
			m_pNervusLines[j] = new CNervus(new CNervusAntenna( ), new CNervusAntenna( ));
			NervusPOS pos = {j,0};
			m_pNervusLines[j]->SetPosition( pos );
		}
	};
	~CNervusNet(void)
	{
		for( int j=0; j<NERVUS_NET_DEPTH; j++ )
		{
			delete m_pNervusLines[j];
		}
	};

	void DisplayNervusMap( void )
	{
		for( int j=0; j<NERVUS_NET_DEPTH; j++ )
		{
			CNervus *p = m_pNervusLines[j];
			while( p )
			{
				int idx = 0;
				CNervusAntenna* a = p->GetFirstOutputAntenna();
				while( a )
				{
					if( a->GetPeer() && a->GetPeer()->GetMyNervus() )
						TRACE("nv[%d,%d], %d, connect to [%d,%d], rate: %f\n", p->GetPosition().nLine, p->GetPosition().nIdx, idx, a->GetPeer()->GetMyNervus()->GetPosition().nLine, a->GetPeer()->GetMyNervus()->GetPosition().nIdx, a->GetRate() );
					else
						TRACE("nv[%d,%d], %d, connect to NULL\n", p->GetPosition().nLine, p->GetPosition().nIdx, idx);
					a = a->GetNextAntenna();
					idx++;
				}
				p = p->pNextInLine;
			}
		}
	};

	void AddNervusAtLine( int nLine, CNervus *pNervus )
	{
		int nIdx = 0;
		CNervus *pLastNervus = m_pNervusLines[nLine];
		while( pLastNervus->pNextInLine )
		{
			pLastNervus = pLastNervus->pNextInLine;
			nIdx++;
		}
		pLastNervus->pNextInLine = pNervus;
		NervusPOS pos = {nLine,nIdx+1};
		pNervus->SetPosition( pos );
	}

	CNervus *GetFirstNervusOfLine( int nLine )
	{
		return m_pNervusLines[nLine];
	}

	CNervus *GetNervusAtPos( NervusPOS pos )
	{
		CNervus *pNervus = m_pNervusLines[pos.nLine];
		do
		{
			if( pos.nIdx == pNervus->GetPosition().nIdx && pos.nLine == pNervus->GetPosition().nLine )
				return pNervus;
		}while( pNervus = pNervus->pNextInLine );
		return NULL;
	}

	bool MakeConnection( NervusPOS posSource, int nSourcePinIdx, NervusPOS posTarget )
	{
		if( GetNervusAtPos( posSource ) && GetNervusAtPos( posTarget ) )
		{
			return GetNervusAtPos( posSource )->ConnectNervus( nSourcePinIdx, GetNervusAtPos(posTarget) );
		}
		else
			return false;
	};

	bool SetOutputAntennaRate( NervusPOS posSource, int nSourcePinIdx, float fRate )
	{
		if( GetNervusAtPos( posSource ) )
		{
			GetNervusAtPos( posSource )->GetOutputAntenna( nSourcePinIdx )->SetRate( fRate );
			return true;
		}
		else
			return false;
	};

	CNervus *GetNervus( int nLine, int nIdx )
	{
		NervusPOS pos = {nLine, nIdx};
		return GetNervusAtPos( pos );
	}

	void ProcessSignal( void );

private:
	CNervus *m_pNervusLines[NERVUS_NET_DEPTH];
};

typedef struct _NervusLink{
	NervusPOS posSource;
	int nSourcePinIdx;
	NervusPOS posTarget;
} NervusLink;

typedef struct _OutputAntennaRate{
	NervusPOS posSource;
	int nSourcePinIdx;
	float fRate;
} OutputAntennaRate;